#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from fastapi import FastAPI, Request, Form, BackgroundTasks, Response, HTTPException, UploadFile, File
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import os
import uuid
import asyncio
from pathlib import Path
from typing import Dict
import json
import urllib.parse
import time
import aiohttp
import re
import shutil
import sqlite3
from datetime import datetime

# 导入BBDown核心模块
from bbdown.core.parser import Parser
from bbdown.core.config import Config
from bbdown.core.logger import Logger
from bbdown.utils.downloader import Downloader
from bbdown.utils.merger import Merger
from bbdown.utils.danmaku import DanmakuUtil
from bbdown.utils.subtitle import SubtitleUtil
from web.utils.log_decorator import log_operation, log_task, GLOBAL_USER_ID

# 导入常量
from web.modules.constants import (
    current_dir,
    UPLOAD_DIR,
    CUT_DIR,
    DOWNLOAD_DIR,
    DB_PATH,
    TASKS,
    CUT_TASKS,
    BATCH_CUT_TASKS
)

# 导入数据库函数
from web.modules.db import (
    init_db,
    insert_task,
    update_task_status,
    get_task,
    insert_log,
    insert_result,
    update_task_filename,
    update_result_filename,
    get_task_files,
    get_tasks,
    get_logs,
    get_log,
    get_file_info
)

# 导入清理函数
from web.modules.cleanup import cleanup_temp_files, cleanup_batch_cut_files

# 导入下载函数
from web.modules.download import download_task, parse_video_info

# 导入设置函数
from web.modules.settings import (
    verify_cookie,
    check_cookie_status,
    save_cookie,
    get_cookie,
    set_test_invalid_cookie,
    verify_and_save_cookie
)

# 导入裁剪函数
from web.modules.cut import (
    cut_video,
    cut_task,
    get_cut_progress,
    get_cut_file,
    rename_cut_file,
    get_cut_preview
)

# 导入批量裁剪函数
from web.modules.batch_cut import (
    batch_upload_video,
    batch_cut_video,
    get_batch_cut_status,
    get_batch_cut_file,
    batch_download_files
)

app = FastAPI()

# 配置静态文件和模板
app.mount("/static", StaticFiles(directory=os.path.join(current_dir, "static")), name="static")
templates = Jinja2Templates(directory=os.path.join(current_dir, "templates"))

@app.on_event("startup")
async def startup_event():
    """
    应用启动时的初始化函数
    
    功能：
    1. 初始化数据库
    2. 启动定期清理临时文件的任务
    """
    init_db()
    # 启动清理任务
    asyncio.create_task(cleanup_temp_files())

@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    """
    首页路由
    
    返回：
        HTMLResponse: 渲染后的index.html页面
    """
    return templates.TemplateResponse("index.html", {"request": request})

@app.get("/download", response_class=HTMLResponse)
async def download_page(request: Request):
    """
    下载页面路由
    
    返回：
        HTMLResponse: 渲染后的download.html页面
    """
    return templates.TemplateResponse("video/download.html", {"request": request})

@app.get("/cut", response_class=HTMLResponse)
async def cut_page(request: Request):
    """
    视频裁剪页面路由
    
    返回：
        HTMLResponse: 渲染后的cut.html页面
    """
    return templates.TemplateResponse("edit/cut.html", {"request": request})

@app.get("/cut-video", response_class=HTMLResponse)
async def cut_video_page(request: Request):
    """
    批量视频裁剪页面路由
    
    返回：
        HTMLResponse: 渲染后的cut_video.html页面
    """
    return templates.TemplateResponse("edit/cut_video.html", {"request": request})

@app.get("/settings", response_class=HTMLResponse)
async def settings_page(request: Request):
    """
    设置页面路由
    
    返回：
        HTMLResponse: 渲染后的settings.html页面
    """
    return templates.TemplateResponse("system/settings.html", {"request": request})

@app.get("/task-list", response_class=HTMLResponse)
async def task_list_page(request: Request):
    """
    任务列表页面路由
    
    返回：
        HTMLResponse: 渲染后的task_list.html页面
    """
    return templates.TemplateResponse("log/task_list.html", {"request": request})

@app.get("/log-list", response_class=HTMLResponse)
async def log_list_page(request: Request):
    """
    日志列表页面路由
    
    返回：
        HTMLResponse: 渲染后的log_list.html页面
    """
    return templates.TemplateResponse("log/log_list.html", {"request": request})

@app.get("/task-detail/{task_id}", response_class=HTMLResponse)
async def task_detail_page(request: Request, task_id: str):
    """
    任务详情页面路由
    
    参数：
        task_id (str): 任务ID
        
    返回：
        HTMLResponse: 渲染后的task_detail.html页面，包含任务ID信息
    """
    return templates.TemplateResponse("log/task_detail.html", {"request": request, "task_id": task_id})

@app.post("/api/parse")
@log_operation("parse_video")
async def parse_video(url: str = Form(...)):
    """
    解析视频信息
    
    参数：
        url (str): 视频URL
        
    返回：
        dict: 包含视频信息的字典
    """
    try:
        return await parse_video_info(url)
    except Exception as e:
        return JSONResponse(
            status_code=400,
            content={"error": str(e)}
        )

@app.post("/api/download")
@log_task("download")
@log_operation("download_video")
async def download_video(
    background_tasks: BackgroundTasks,
    url: str = Form(...),
    quality: str = Form(...),
    cid: str = Form(...),
    danmaku: bool = Form(False),
    subtitle: bool = Form(False)
):
    """
    开始下载视频
    
    参数：
        background_tasks (BackgroundTasks): 后台任务管理器
        url (str): 视频URL
        quality (str): 视频质量
        cid (str): 分P ID
        danmaku (bool): 是否下载弹幕
        subtitle (bool): 是否下载字幕
        
    返回：
        dict: 包含任务ID和状态的字典
    """
    logger = Logger()
    logger.info(f"开始下载任务")
    logger.info(f"URL: {url}")
    logger.info(f"分P ID: {cid}")
    logger.info(f"清晰度: {quality}")
    logger.info(f"下载弹幕: {danmaku}")
    logger.info(f"下载字幕: {subtitle}")
    
    task_id = str(uuid.uuid4())
    TASKS[task_id] = {
        "status": "started",
        "progress": 0,
        "video_progress": (0, 1),
        "audio_progress": (0, 1),
        "video_speed": 0,
        "audio_speed": 0,
        "total_speed": 0,
        "start_time": time.time(),
        "current_step": "准备下载"
    }
    
    # 添加后台任务
    background_tasks.add_task(
        download_task,
        task_id,
        url,
        quality,
        cid,
        danmaku,
        subtitle
    )
    
    return {"task_id": task_id, "status": "started"}

@app.get("/api/progress/{task_id}")
async def get_progress(task_id: str):
    """
    获取下载进度，TASKS中存储下载任务进度信息，通过update_progress回调设置状态
    
    参数：
        task_id (str): 任务ID
        
    返回：
        dict: 包含任务进度信息的字典
    """
    if task_id not in TASKS:
        return JSONResponse(
            status_code=404,
            content={"error": "任务不存在"}
        )
    
    task = TASKS[task_id]
    return {
        "progress": task["progress"],
        "status": task["status"],
        "error": task.get("error"),
        "current_step": task.get("current_step", ""),
        "video_progress": task.get("video_progress", (0, 1)),
        "audio_progress": task.get("audio_progress", (0, 1)),
        "video_speed": task.get("video_speed", 0),
        "audio_speed": task.get("audio_speed", 0),
        "total_speed": task.get("total_speed", 0)
    }

@app.get("/api/file/{task_id}")
def get_file(task_id: str):
    """
    获取下载的文件，返回文件下载对象FileResponse
    
    参数：
        task_id (str): 任务ID
        
    返回：
        FileResponse: 文件响应
    """
    filename = TASKS.get(task_id, {}).get('final_filename')
    if not filename:
        filename = f"{task_id}.mp4"
    file_path = DOWNLOAD_DIR / filename
    if file_path.exists():
        return FileResponse(
            str(file_path),
            filename=filename,
            media_type="video/mp4"
        )
    return JSONResponse(
        status_code=404,
        content={"error": "文件不存在"}
    )

@app.post("/api/cookie")
@log_operation("set_cookie")
async def set_cookie(cookie: str = Form(...)):
    """
    设置cookie
    
    参数：
        cookie (str): 用户提供的cookie字符串
        
    返回：
        dict: 包含设置结果和用户信息的字典
    """
    try:
        result = await verify_cookie(cookie)
        save_cookie(cookie)
        return result
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.get("/api/cookie/status")
async def get_cookie_status():
    """
    获取cookie状态
    
    返回：
        dict: 包含cookie状态和用户信息的字典
    """
    return await check_cookie_status()

@app.post("/api/cookie/test/invalid")
async def test_invalid_cookie():
    """
    测试无效cookie
    
    返回：
        dict: 包含测试结果的字典
    """
    try:
        # 保存一个无效的cookie
        invalid_cookie = "SESSDATA=invalid;bili_jct=invalid;DedeUserID=invalid"
        save_cookie(invalid_cookie)
        return {
            "status": "success",
            "message": "已设置无效的测试Cookie"
        }
    except Exception as e:
        return JSONResponse(
            status_code=400,
            content={"error": f"设置测试Cookie失败: {str(e)}"}
        )

@app.post("/api/cookie/verify")
async def verify_cookie_endpoint(cookie: str = Form(...)):
    """验证cookie是否有效"""
    return await verify_and_save_cookie(cookie)

@app.get("/api/image/{url:path}")
async def get_image(url: str):
    """获取图片"""
    try:
        # 解码URL
        url = urllib.parse.unquote(url)
        # 获取图片内容
        image_data = await parser._get_image(url)
        if image_data:
            return Response(content=image_data, media_type="image/png")
        else:
            raise HTTPException(status_code=404, detail="图片获取失败")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.post("/api/cut")
@log_task("cut")
@log_operation("cut_video")
async def cut_video_endpoint(
    request: Request,
    background_tasks: BackgroundTasks,
    video: UploadFile = File(...),
    start_time: str = Form(...),
    end_time: str = Form(...),
    user_id: str = Form(None)
):
    """处理视频裁剪请求"""
    try:
        content = await video.read()
        result = await cut_video(content, video.filename, start_time, end_time, user_id)
        return result
    except Exception as e:
        logger.error(f"处理裁剪请求失败: {str(e)}")
        return {"error": str(e)}

@app.get("/api/cut/progress/{task_id}")
async def cut_progress_endpoint(task_id: str):
    """获取裁剪进度"""
    try:
        return get_cut_progress(task_id)
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.get("/api/cut/file/{task_id}")
async def get_cut_file_endpoint(task_id: str):
    """获取裁剪后的视频文件"""
    try:
        return get_cut_file(task_id)
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.post("/api/cut/rename/{task_id}")
@log_operation("rename_file")
async def rename_cut_file_endpoint(task_id: str, new_name: str = Form(...)):
    """重命名裁剪后的视频文件"""
    logger = Logger()
    logger.info(f"收到重命名请求: task_id={task_id}, new_name={new_name}")
    try:
        return rename_cut_file(task_id, new_name)
    except HTTPException as e:
        logger.error(f"重命名失败: {e.detail}")
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.get("/api/cut/preview/{task_id}")
async def get_cut_preview_endpoint(task_id: str):
    """获取视频预览信息"""
    try:
        return await get_cut_preview(task_id)
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.post("/api/batch-cut/upload")
async def batch_upload_video_endpoint(video: UploadFile = File(...)):
    """上传视频文件用于批量裁剪"""
    try:
        content = await video.read()
        return await batch_upload_video(content, video.filename)
    except Exception as e:
        return JSONResponse(
            status_code=400,
            content={"error": str(e)}
        )

@app.post("/api/batch-cut")
@log_task("batch_cut")
@log_operation("batch_cut_video")
async def batch_cut_video_endpoint(
    request: Request,
    background_tasks: BackgroundTasks,
    video_id: str = Form(...),
    tasks: str = Form(...),
    user_id: str = Form(None)
):
    """处理批量裁剪请求"""
    return await batch_cut_video(video_id, tasks, user_id)

@app.get("/api/batch-cut/status/{task_id}")
async def batch_cut_status_endpoint(task_id: str):
    """获取批量裁剪任务状态"""
    try:
        return get_batch_cut_status(task_id)
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.get("/api/batch-cut/file/{task_id}/{filename}")
async def get_batch_cut_file_endpoint(task_id: str, filename: str):
    """获取单个裁剪后的视频文件"""
    try:
        return get_batch_cut_file(task_id, filename)
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.post("/api/batch-cut/download-batch")
async def batch_download_files_endpoint(files: str = Form(...)):
    """批量下载多个任务的视频文件"""
    try:
        return await batch_download_files(files)
    except HTTPException as e:
        return JSONResponse(
            status_code=e.status_code,
            content={"error": e.detail}
        )

@app.post("/api/batch-cut/cleanup/{video_id}")
async def batch_cut_cleanup(video_id: str):
    """清理上传的视频文件"""
    return await cleanup_batch_cut_files(video_id)

@app.get("/api/task/{task_id}")
async def get_task_result(task_id: str):
    """
    获取任务结果
    
    参数：
        task_id (str): 任务ID
        
    返回：
        dict: 包含以下信息：
            - task_id: 任务ID
            - user_id: 用户ID
            - type: 任务类型
            - params: 任务参数
            - status: 任务状态
            - result: 任务结果
            - created_at: 创建时间
            - updated_at: 更新时间
            - download_files: 下载文件列表
            
    错误：
        - 404: 任务不存在
    """
    row = get_task(task_id)
    if not row:
        return JSONResponse(status_code=404, content={"error": "任务不存在"})
        
    result = json.loads(row[5]) if row[5] else None
    download_files = get_task_files(task_id)
    
    return {
        "task_id": row[0],
        "user_id": row[1],
        "type": row[2],
        "params": json.loads(row[3]) if row[3] else None,
        "status": row[4],
        "result": result,
        "created_at": row[6],
        "updated_at": row[7],
        "download_files": download_files
    }

@app.get("/api/tasks")
async def api_tasks(q: str = '', page: int = 1, page_size: int = 20):
    """
    获取任务列表
    
    参数：
        q (str): 搜索关键词，用于搜索任务ID、类型或状态
        page (int): 页码，从1开始
        page_size (int): 每页显示的任务数量
        
    返回：
        dict: 包含以下信息：
            - total: 总任务数
            - page: 当前页码
            - page_size: 每页数量
            - data: 任务列表，每个任务包含完整信息和相关文件
    """
    return get_tasks(q, page, page_size)

@app.get("/api/logs")
async def api_logs(q: str = '', page: int = 1, page_size: int = 20):
    """
    获取操作日志列表
    
    参数：
        q (str): 搜索关键词，用于搜索操作类型或详情
        page (int): 页码，从1开始
        page_size (int): 每页显示的日志数量
        
    返回：
        dict: 包含以下信息：
            - total: 总日志数
            - page: 当前页码
            - page_size: 每页数量
            - data: 日志列表
    """
    return get_logs(q, page, page_size)

@app.get("/api/log/{log_id}")
async def api_log(log_id: int):
    """
    获取单个操作日志
    
    参数：
        log_id (int): 日志ID
        
    返回：
        dict: 包含以下信息：
            - id: 日志ID
            - user_id: 用户ID
            - task_id: 关联的任务ID
            - action: 操作类型
            - detail: 操作详情
            - ip: 用户IP地址
            - created_at: 创建时间
            
    错误：
        - 404: 日志不存在
    """
    log = get_log(log_id)
    if not log:
        return JSONResponse(status_code=404, content={"error": "日志不存在"})
    return log

@app.get("/api/download/file/{task_id}/{file_name}")
async def download_file(task_id: str, file_name: str):
    """
    下载文件
    
    参数：
        task_id (str): 任务ID
        file_name (str): 文件名
        
    返回：
        FileResponse: 文件响应，包含文件内容和下载信息
        
    错误：
        - 404: 文件不存在
    """
    file_info = get_file_info(task_id, file_name)
    if not file_info:
        return JSONResponse(status_code=404, content={"error": "文件不存在"})
        
    file_path, file_type = file_info
    if not os.path.exists(file_path):
        return JSONResponse(status_code=404, content={"error": "文件不存在"})
        
    return FileResponse(file_path, filename=file_name, media_type=file_type) 